/*
 ============================================================================
 Name        : usbi2c.c
 Author      : Victor Gaspar
 Version     :
 Copyright   : 
 Description : Execute ACTIONs in the USBI2C bus
 ============================================================================
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "actions.h"

#define MSGSZ     128
#define TIMEOUT_SEC 2

/* Declare the message structure. */
typedef struct {
	long mtype;
	char mtext[MSGSZ];
} message_buf;

#define LOCK_FILE "/tmp/usbi2cd.lock"
#define LOG_FILE  "/tmp/usbi2c.log"

void log_message(char* filename, char* message) {
	FILE *logfile;
	logfile = fopen(filename, "a");
	if (!logfile)
		return;
	fprintf(logfile, "%s\n", message);
	fclose(logfile);
}

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
// Union already defined in sys/sem.h
#else
// Define union
union semun {
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};
#endif

#define LENGTH(x) (sizeof(x)/sizeof(*(x)))

void help(){
	printf("usbi2c error, argument ACTION is missing:\n");
	printf("usage:  usbi2c ACTION\n");
}

int selectAction(char* firstparam){
	int i;
	for(i=0; i<LENGTH(tab); i++){
		if(strcmp(firstparam, tab[i].action) == 0){
			return i;
		}
	}
	return -1;
}

int main(int argc, char* argv[]) {

	if(argc < 2){
		help();
		exit(-1);
	}

	/* Select the ACTION */
	int actionnum = selectAction(argv[1]);
	if(actionnum < 0){
		log_message(LOG_FILE, "ACTION does not exist");
		exit(-1);
	}

	int msqidCommandQueue;
	int msqidResponseQueue;
	key_t keyCommandQueue;
	key_t keyResponseQueue;
	int msgflg = 0666;
	message_buf sbuf;
	message_buf rbuf;
	size_t buf_length;

	/*
	 * Get the message queue id for the
	 * "name" 1234, which was created by
	 * the server.
	 */
	keyCommandQueue = 1234;
	keyResponseQueue = 4321;

	log_message(LOG_FILE, "Getting Command Queue");

	if ((msqidCommandQueue = msgget(keyCommandQueue, msgflg)) < 0) {
		log_message(LOG_FILE, "ERROR: Getting Command Queue");
		exit(-1);
	} else
		log_message(LOG_FILE, "Getting Command Queue succeed!");

	log_message(LOG_FILE, "Getting Response Queue");

	if ((msqidResponseQueue = msgget(keyResponseQueue, msgflg)) < 0) {
		log_message(LOG_FILE, "ERROR: Getting Response Queue");
		exit(-1);
	} else
		log_message(LOG_FILE, "Getting Response Queue succeed!");

	union semun arg;
	key_t semaphoreKey;
	int semid;
	struct sembuf lockop, unlockop;

	lockop.sem_num = 0;
	lockop.sem_op = -1;
	lockop.sem_flg = 0;

	unlockop.sem_num = 0;
	unlockop.sem_op = 1;
	unlockop.sem_flg = 0;

	semaphoreKey = ftok(LOCK_FILE, 33);
	if (semaphoreKey == (key_t) -1) {
		log_message(LOG_FILE, "ERROR: The daemon is not initialized!");
		exit(-1);
	}

	/* If Semaphore does not exist - Create it.
	 * If Semaphore exist - get the ID
	 * */
	if ((semid = semget(semaphoreKey, 1, IPC_CREAT | IPC_EXCL | 0600)) != -1) {
		/* Just one concurrent process at a time*/
		arg.val = 1;
		semctl(semid, 0, SETVAL, &arg);

		/* Set to "green" */
		if (semop(semid, &unlockop, 1) == -1) {
			semctl(semid, 0, IPC_RMID, 0);
			log_message(LOG_FILE, "ERROR: Semaphore set to green");
			exit(-1);
		}
	} else if (errno == EEXIST) {
		semid = semget(semaphoreKey, 0, 0);
	}

	/*
	 * We'll send message type 1
	 */
	sbuf.mtype = 1;

	/* Semaphore lock */
	if (semop(semid, &lockop, 1) == -1) {
		semctl(semid, 0, IPC_RMID, 0);
		log_message(LOG_FILE, "ERROR: Lock failed");
		exit(-1);
	}

	/************** Loop for all MESSAGES for the given ACTION *****************/
	int i;
	for (i = 0; i < tab[actionnum].num_messages; i++) {
		(void) strcpy(sbuf.mtext, tab[actionnum].message[i]);

		buf_length = strlen(sbuf.mtext) + 1;

		/*
		 * Send a message.
		 */
		if (msgsnd(msqidCommandQueue, &sbuf, buf_length, IPC_NOWAIT) < 0) {
			log_message(LOG_FILE, "ERROR: Sending message to the Command Queue");
			exit(-1);
		} else {
			log_message(LOG_FILE, "Message Sent!");

			msgrcv(msqidResponseQueue, &rbuf, MSGSZ, 1, 0);
			printf("%s\n", rbuf.mtext);
		}
	}
	/*******************************/

	/* Semaphore unlock */
	if (semop(semid, &unlockop, 1) == -1) {
		semctl(semid, 0, IPC_RMID, 0);
		log_message(LOG_FILE, "ERROR: Un-Lock failed");
		exit(-1);
	}

	/* If the semaphore status is "green" (i.e. ==0), then remove it */
	/* This means that we are the last process using the semaphore */
	int value;
	if ((value = semctl(semid, 0, GETVAL, 0)) == 1) {
		semctl(semid, 0, IPC_RMID, 0);
		log_message(LOG_FILE, "Semaphore removed");
	} else if (value == -1) {
		semctl(semid, 0, IPC_RMID, 0);
		log_message(LOG_FILE, "GETVAL: failed to retrieve semaphore value");
		exit(-1);
	}

	exit(0);
}
